/**
 * 
 */
package com.ibuonline.search.solr.dict.file.sougou;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.ibuonline.search.solr.dict.Dict;
import com.ibuonline.search.solr.dict.DictReader;

/**
 * @author Liuye
 * 
 */
public class ScelDictReader implements DictReader {
	private InputStream inputStream;
	public static final String encoding = "UTF-16LE";

	/**
	 * Creates a <code>TxtDictReader</code> by opening a connection to an actual
	 * file, the file named by the path name <code>name</code> in the file
	 * system.
	 * <p>
	 * If the named file does not exist, inputStream a directory rather than a
	 * regular file, or for some other reason cannot be opened for reading then
	 * a <code>FileNotFoundException</code> inputStream thrown.
	 * 
	 * @param name
	 *            the system-dependent file name.
	 * @throws IOException
	 */
	public ScelDictReader(String name) throws IOException {
		this(name != null ? new File(name) : null);
	}

	/**
	 * Creates a <code>FileInputStream</code> by opening a connection to an
	 * actual file, the file named by the <code>File</code> object
	 * <code>file</code> in the file system.
	 * <p>
	 * If the named file does not exist, inputStream a directory rather than a
	 * regular file, or for some other reason cannot be opened for reading then
	 * a <code>FileNotFoundException</code> inputStream thrown.
	 * 
	 * @param file
	 *            the file to be opened for reading.
	 * @throws IOException
	 * @see java.io.File#getPath()
	 */
	public ScelDictReader(File file) throws IOException {
		this(new FileInputStream(file));
	}

	public ScelDictReader(InputStream in) throws IOException {
		this.inputStream = in;
	}

	@Override
	public Dict read() {
		ScelDict model = new ScelDict();
		DataInputStream input = new DataInputStream(inputStream);
		int read;
		try {
			byte[] bytes = new byte[4];
			input.readFully(bytes);
			assert (bytes[0] == 0x40 && bytes[1] == 0x15 && bytes[2] == 0 && bytes[3] == 0);
			input.readFully(bytes);
			int flag1 = bytes[0];
			assert (bytes[1] == 0x43 && bytes[2] == 0x53 && bytes[3] == 0x01);
			int[] reads = new int[] { 8 };
			model.setName(readString(input, 0x130, reads));
			model.setType(readString(input, 0x338, reads));
			model.setDescription(readString(input, 0x540, reads));
			model.setSample(readString(input, 0xd40, reads));
			read = reads[0];
			input.skip(0x1540 - read);
			read = 0x1540;
			input.readFully(bytes);
			read += 4;
			assert (bytes[0] == (byte) 0x9D && bytes[1] == 0x01
					&& bytes[2] == 0 && bytes[3] == 0);
			bytes = new byte[128];
			Map<Integer, String> pyMap = new LinkedHashMap<Integer, String>();
			while (true) {
				int mark = readUnsignedShort(input);
				int size = input.readUnsignedByte();
				input.skip(1);
				read += 4;
				assert (size > 0 && (size % 2) == 0);
				input.readFully(bytes, 0, size);
				read += size;
				String py = new String(bytes, 0, size, encoding);
				// System.out.println(py);
				pyMap.put(mark, py);
				if ("zuo".equals(py)) {
					break;
				}
			}
			if (flag1 == 0x44) {
				input.skip(0x2628 - read);
			} else if (flag1 == 0x45) {
				input.skip(0x26C4 - read);
			} else {
				throw new RuntimeException("出现意外，联系作者");
			}
			StringBuffer buffer = new StringBuffer();
			Map<String, List<String>> wordMap = new LinkedHashMap<String, List<String>>();
			while (true) {
				int size = readUnsignedShort(input);
				if (size < 0) {
					break;
				}
				int count = readUnsignedShort(input);
				int len = count / 2;
				assert (len * 2 == count);
				buffer.setLength(0);
				for (int i = 0; i < len; i++) {
					int key = readUnsignedShort(input);
					buffer.append(pyMap.get(key)).append("'");
				}
				buffer.setLength(buffer.length() - 1);
				String py = buffer.toString().trim();
				List<String> list = wordMap.get(py);
				if (list == null) {
					list = new ArrayList<String>();
					wordMap.put(py, list);
				}
				for (int i = 0; i < size; i++) {
					count = readUnsignedShort(input);
					if (count > bytes.length) {
						bytes = new byte[count];
					}
					input.readFully(bytes, 0, count);
					String word = new String(bytes, 0, count, encoding);
					// 接下来12个字节可能是词频或者类似信息
					input.skip(12);
					list.add(word.trim());
				}
			}
			// System.out.println(wordMap.size());
			model.setWordMap(wordMap);
			return model;
		} catch (Exception e) {
			throw new RuntimeException("词库文件格式有误，无法读取。");
		}
	}

	protected final int readUnsignedShort(InputStream in) throws IOException {
		int ch1 = in.read();
		int ch2 = in.read();
		if ((ch1 | ch2) < 0) {
			return Integer.MIN_VALUE;
		}
		return (ch2 << 8) + (ch1 << 0);
	}

	protected String readString(DataInputStream input, int pos, int[] reads)
			throws IOException {
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		int read = reads[0];
		input.skip(pos - read);
		read = pos;
		output.reset();
		while (true) {
			int c1 = input.read();
			int c2 = input.read();
			read += 2;
			if (c1 == 0 && c2 == 0) {
				break;
			} else {
				output.write(c1);
				output.write(c2);
			}
		}
		reads[0] = read;
		return new String(output.toByteArray(), encoding);
	}

	@Override
	public void close() throws IOException {
		this.inputStream.close();
	}
}
